﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace TTApp.Phone.Silverlight.Hexagons
{
    /// <summary>
    /// Cung cấp các sự kiện, phương thức cơ bản cho 1 game.
    /// Một game bao giờ cũng có 1 bàn cờ.
    /// 
    /// Lớp kế thừa override phương thức GetNextPlayer() để thay đổi thứ tự người chơi so với thứ tự mặc định
    /// (tuần tự).
    /// 
    /// Khi muốn chuyển lượt sang cho người chơi tiếp, gọi phương thức ChangeTurn().
    /// 
    /// Game sẽ throw Exception nếu khi bắt đầu mà không có người chơi nào trong _playerList.
    /// Người chơi bắt đầu mặc định là người đứng đầu tiên trong danh sách.
    /// 
    /// Cần phải gọi StartGame() để game được bắt đầu.
    /// </summary>
    public abstract partial class GameBase : UserControl
    {
        #region //----------------------------- Class Private Fields --------------------------------------//
        /// <summary>
        /// Bàn cờ cho màn chơi hiện tại.
        /// </summary>
        protected Board _board;

        /// <summary>
        /// List chứa các người chơi cho màn chơi hiện tại.
        /// </summary>
        protected List<Player> _playerList = new List<Player>();

        /// <summary>
        /// Người chơi ở lượt đi hiện tại.
        /// </summary>
        protected Player _currentPlayer;

        /// <summary>
        /// Biến đánh dấu là game đã bắt đầu.
        /// </summary>
        protected bool _isGameStarted;

        /// <summary>
        /// Số lượng lượt đã đi của game.
        /// </summary>
        private int _turnCount;

        /// <summary>
        /// Có tự động zoom khi người chơi chạm vào Hexagon hay không.
        /// </summary>
        protected bool _autoZoom = false;

        /// <summary>
        /// Đường dẫn đến file XML chứa dữ liệu của Board và Players.
        /// </summary>
        protected string _xmlDataFile;

        /// <summary>
        /// Kiểm tra xem map (_xmlDataFile) đã được gán hay chưa.
        /// </summary>
        protected bool _isMapSet = false;

        /// <summary>
        /// Đối tượng Gameplay đi kèm với Game.
        /// </summary>
        private Gameplay _gameplay;
        #endregion

        #region //----------------------------- Class Protected Fields ------------------------------------//
        protected SoundController soundController;
        #endregion

        #region //----------------------------- Class Public Constructors ---------------------------------//
        /// <summary>
        /// Tạo 1 đối tượng GameBase mới.
        /// </summary>
        public GameBase()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(OnLoaded);
        }
        #endregion

        #region //----------------------------- Class CLR Properties --------------------------------------//
        /// <summary>
        /// Kiểm tra xem game đã được bắt đầu hay chưa.
        /// </summary>
        public bool IsGameStarted
        {
            get { return _isGameStarted; }
        }

        /// <summary>
        /// Số lượng lượt đã đi của game.
        /// </summary>
        public int TurnCount
        {
            get
            {
                return _turnCount;
            }
            protected set
            {
                _turnCount = value;
            }
        }

        /// <summary>
        /// Lấy hoặc gán giá trị điều chỉnh việc tự động zoom bàn cờ khi người chơi chạm vào Hexagon.
        /// </summary>
        protected bool AutoZoom
        {
            get { return _autoZoom; }
            set { _autoZoom = value; }
        }

        /// <summary>
        /// Lấy đối tượng bàn cờ của game. Mỗi game chỉ có duy nhất 1 bàn cờ.
        /// </summary>
        public Board Board
        {
            get { return _board; }
        }

        /// <summary>
        /// Lấy đối tượng người chơi hiện tại.
        /// </summary>
        public Player CurrentPlayer
        {
            get { return _currentPlayer; }
        }

        /// <summary>
        /// SoundController của Thế Anh.
        /// </summary>
        public SoundController SoundController
        {
            get
            {
                return soundController;
            }
        }

        /// <summary>
        /// Đối tượng Gameplay đi kèm với Game.
        /// </summary>
        public Gameplay Gameplay
        {
            get { return _gameplay; }
            set { _gameplay = value; }
        }
        #endregion

        #region //----------------------------- Public Event Delegates ------------------------------------//
        /// <summary>
        /// Phát ra khi lượt của người chơi thay đổi.
        /// </summary>
        public event EventHandler TurnChanged;

        /// <summary>
        /// Phát ra khi người chơi của lượt hiện tại thay đổi.
        /// </summary>
        public event EventHandler<PlayerChangedEventArgs> PlayerChanged;

        /// <summary>
        /// Phát ra khi game sắp sửa hoàn thành xong, chuẩn bị vào trạng thái Started.
        /// </summary>
        public event EventHandler GameStarting;

        /// <summary>
        /// Phát ra khi game đã được bắt đầu.
        /// </summary>
        public event EventHandler GameStarted;

        /// <summary>
        /// Phát ra khi lượt hiện tại kết thúc, mọi animation đã hoàn thành và lượt chuẩn bị
        /// chuyển sang cho người chơi tiếp theo.
        /// </summary>
        public event EventHandler TurnFinished;

        /// <summary>
        /// Phát ra khi game kết thúc, có người chơi đã chiến thắng hoặc game ở trạng thái hòa.
        /// </summary>
        public event EventHandler GameEnded;
        #endregion

        #region //----------------------------- Private Event Handlers ------------------------------------//
        /// <summary>
        /// Lấy Board nằm trong GestureViewer. 
        /// Đăng kí sự kiện cho Hexagon và HexagonSlot.
        /// </summary>
        private void OnLoaded(object sender, RoutedEventArgs args)
        {
            // Phải gán vì không đặt được tên đối tượng nằm trong UserControl
            _board = viewer.Child as Board;

            // Nạp bàn cờ và người chơi từ file XML.
            _board.Source = Board.GetBoardSourceFromFile(_xmlDataFile);
            _playerList = Player.LoadFile(_xmlDataFile, _board);

            RegisterEvent();
        }

        /// <summary>
        /// Nếu người chơi hiện tại không phải là AI thì cho người chơi thao tác với HexagonSlot.
        /// </summary>
        private void OnHexagonSlotTap(object sender, GestureEventArgs args)
        {
            if (!(_currentPlayer is AIPlayer))
            {
                if (_autoZoom)
                {
                    viewer.Zoom(viewer.PrefferedMinScale);
                }

                OnHexagonSlotTap(sender as HexagonSlot);
            }

            args.Handled = true;
        }

        /// <summary>
        /// Nếu người chơi hiện tại không phải là AI thì chơi người chơi thao tác với Hexagon.
        /// </summary>
        private void OnHexagonTap(object sender, GestureEventArgs args)
        {
            if (!(_currentPlayer is AIPlayer))
            {
                if (_autoZoom)
                {
                    if (_currentPlayer.IsOwn(sender as Hexagon))
                    {
                        Point tapPoint = args.GetPosition(viewer.Child);
                        viewer.Zoom(tapPoint, 1.35, new Duration(TimeSpan.FromSeconds(0.5)));
                    }
                    else
                    {
                        viewer.Zoom(viewer.PrefferedMinScale);
                    }
                }

                OnHexagonTap(sender as Hexagon);
            }

            args.Handled = true;
        }

        /// <summary>
        /// Cung cấp thêm phương thức xử lý sự kiện nếu Board được Tap.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnBoardTap(object sender, GestureEventArgs args)
        {
            OnBoardTap();
        }
        #endregion

        #region //----------------------------- Protected Event Invokers ----------------------------------//
        /// <summary>
        /// Được gọi khi lượt đánh của người chơi thay đổi.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnTurnChanged(EventArgs args)
        {
            if (TurnChanged != null)
            {
                TurnChanged(this, args);
            }
        }

        /// <summary>
        /// Được gọi khi lượt hiện tại kết thúc, mọi animation đã hoàn thành. Lượt đã được chuyển sang cho 
        /// người chơi tiếp theo.
        /// </summary>
        /// <param name="args">Empty.</param>
        protected virtual void OnTurnFinished(EventArgs args)
        {
            ChangeTurn();

            if (TurnFinished != null)
            {
                TurnFinished(this, args);
            }

            if (CheckGameState() != GameState.NotFinished)
            {
                OnGameEnded(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Phát ra khi người chơi ở lượt hiện tại thay đổi.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnPlayerChanged(PlayerChangedEventArgs args)
        {
            if (PlayerChanged != null)
            {
                PlayerChanged(this, args);
            }
        }

        /// <summary>
        /// Phát ra khi game sắp sửa được bắt đầu. Ngay trước khi game chuyển sang trạng thái Started.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnGameStarting(EventArgs args)
        {
            if (GameStarting != null)
            {
                GameStarting(this, args);
            }
        }

        /// <summary>
        /// Phát ra khi game đã được bắt đầu.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnGameStarted(EventArgs args)
        {
            if (GameStarted != null)
            {
                GameStarted(this, args);
            }
        }

        /// <summary>
        /// Được gọi khi game đã kết thúc, có người chơi thắng, thua hoặc hòa.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnGameEnded(EventArgs args)
        {
            if (GameEnded != null)
            {
                GameEnded(this, args);
            }
        }
        #endregion

        #region //----------------------------- Class Public Methods --------------------------------------//
        /// <summary>
        /// Bắt đầu chơi game.
        /// </summary>
        public void StartGame()
        {
            if (_isGameStarted)
            {
                throw new InvalidOperationException("Game đã bắt đầu, không thể bắt đầu lại.");
            }

            OnGameStarting(EventArgs.Empty);

            if (_playerList.Count == 0)
            {
                throw new Exception("Game không thể bắt đầu khi không có người chơi.");
            }

            _isGameStarted = true;

            // Mặc định người chơi đầu tiên là người chơi thứ 1 trong danh sách.
            _currentPlayer = _playerList[0];

            OnGameStarted(EventArgs.Empty);
        }
        
        /// <summary>
        /// Chơi lại ván mới.
        /// </summary>
        public void RestartGame()
        {
            // Kiểm tra việc gọi RestartGame() có đúng không.
            if (!_isGameStarted)
            {
                throw new InvalidOperationException("Game chưa chơi, gọi StartGame()");
            }
            _isGameStarted = false;

            // Gán lại các giá trị.
            _board.Source = Board.GetBoardSourceFromFile(_xmlDataFile);
            ModifyPlayerList(_playerList);

            // Đăng kí lại Event cho các đối tượng Hexagon, HexagonSlot... mới.
            RegisterEvent();

            // Chiến thôi :D
            StartGame();
        }
        #endregion

        #region//----------------------------- Class Protected Methods -----------------------------------//
        /// <summary>
        /// Đăng kí sự kiện Tap cho hexagon.
        /// </summary>
        /// <param name="hexagon">Hexagon được đăng kí sự kiện Tap.</param>
        protected void RegisterEvent(Hexagon hexagon)
        {
            hexagon.Tap += new EventHandler<GestureEventArgs>(OnHexagonTap);
        }
        #endregion

        #region //----------------------------- Class Private Methods -------------------------------------//
        /// <summary>
        /// Người chơi hiện tại đã hoàn thành xong lượt đi, chuyển lượt sang cho người chơi khác.
        /// </summary>
        private void ChangePlayer()
        {
            Player oldPlayer = _currentPlayer;
            _currentPlayer = GetNextPlayer();

            OnPlayerChanged(new PlayerChangedEventArgs(oldPlayer, _currentPlayer));
        }

        /// <summary>
        /// Đăng kí sự kiện cho các đối tượng nằm trong board (bao gồm HexagonSlot và Hexagon).
        /// </summary>
        private void RegisterEvent()
        {
            _board.Tap += new EventHandler<GestureEventArgs>(OnBoardTap);

            foreach (HexagonSlot slot in _board.HexagonSlots)
            {
                slot.Tap += new EventHandler<GestureEventArgs>(OnHexagonSlotTap);
                if (slot.Hexagon != null)
                {
                    slot.Hexagon.Tap += new EventHandler<GestureEventArgs>(OnHexagonTap);
                }
            }
        }

        /// <summary>
        /// Chỉnh danh sách người chơi, xóa các Hexagon mà người chơi đang sở hữu, thay bằng các Hexagon
        /// với vị trí tương ứng ở trong file XML.
        /// 
        /// Thực ra, phương thức này chuyển đổi quyền sở hữu Hexagon của người chơi nằm ở 2 list, 1 list 
        /// chứa người chơi cũ (người chơi vừa chơi game vừa xong) và 1 list được tạo từ Player.LoadFile().
        /// </summary>
        /// <param name="oldList">Danh sách được thay đổi.</param>
        /// <remarks>Phương thức này chỉ nên được gọi ở hàm RestartGame().</remarks>
        private void ModifyPlayerList(List<Player> oldList)
        {
            List<Player> newList = Player.LoadFile(_xmlDataFile, _board);

            // Dùng vòng lặp for để lấy đối tượng Player tương ứng ở 2 list.
            for (int i = 0; i < newList.Count; i++)
            {
                // Xóa các Hexagon ở list cũ.
                oldList[i].Hexagons.Clear();
                oldList[i].Score = 0;

                // Thêm Hexagon mới vào đối tượng người chơi cũ.
                while (newList[i].Hexagons.Count > 0)
                {
                    Hexagon hexagon = newList[i].Hexagons[0];
                    newList[i].Release(hexagon);
                    oldList[i].Seize(hexagon);
                }
            }
        }
        #endregion

        #region//----------------------------- Class Virtual Methods -------------------------------------//
        /// <summary>
        /// Lấy người chơi ở lượt tiếp theo.
        /// Lớp kế thừa có thể override phương thức này để thay đổi thứ tự của người chơi.
        /// </summary>
        /// <returns>Người chơi ở lượt chơi kế tiếp.</returns>
        protected virtual Player GetNextPlayer()
        {
            if (_currentPlayer == null)
            {
                throw new InvalidOperationException("Người chơi hiện tại chưa xác định.");
            }

            int index = _playerList.IndexOf(_currentPlayer);
            if (index == -1)
            {
                throw new InvalidOperationException("Người chơi đã bị xóa khỏi màn chơi.");
            }

            return _playerList[(index + 1) % _playerList.Count];
        }

        /// <summary>
        /// Chuyển lượt sang người chơi kế tiếp.
        /// Lớp kế thừa khi override phương thức này nên gọi OnTurnChanged để thông báo cho các đối tượng
        /// khác biết lượt đã được thay đổi.
        /// </summary>
        protected virtual void ChangeTurn()
        {
            ChangePlayer();

            _turnCount++;
            OnTurnChanged(EventArgs.Empty);
        }

        /// <summary>
        /// Xử lý sự kiện _board được tap. Dùng cho lớp kế thừa nếu muốn xử lý sự kiện Board được Tap.
        /// </summary>
        protected virtual void OnBoardTap() { }
        #endregion

        #region //----------------------------- Class Abstract Methods ------------------------------------//
        /// <summary>
        /// Khi được cài đặt, phương thức này sẽ xử lý sự kiện khi 1 HexagonSlot được chạm 
        /// vào bởi người chơi.
        /// </summary>
        /// <param name="tappedSlot">HexagonSlot mà người chơi vừa chạm vào.</param>
        protected abstract void OnHexagonSlotTap(HexagonSlot tappedSlot);

        /// <summary>
        /// Khi được cài đặt, phương thức này sẽ xử lý sự kiện khi 1 Hexagon được chạm
        /// vào bởi người chơi.
        /// </summary>
        /// <param name="tappedHexagon">Hexagon mà vừa người chơi vừa chạm vào.</param>
        protected abstract void OnHexagonTap(Hexagon tappedHexagon);

        /// <summary>
        /// Khi được cài đặt, phương thức này trả về đối tượng người dùng với id được truyền vào.
        /// </summary>
        /// <param name="id">id của người dùng.</param>
        /// <returns>Đối tượng Player tương ứng với id.</returns>
        public abstract Player GetPlayer(int id);

        /// <summary>
        /// Lớp kế thừa cái đặt phương thức này để kiểm tra trạng thái hiện tại của Game: còn đang chơi hay 
        /// đã kết thúc, có người chiến thắng hay hòa.
        /// </summary>
        /// <returns></returns>
        public abstract GameState CheckGameState();

        /// <summary>
        /// Lớp kế thừa cài đặt phương thức này để lấy người chơi chiến thắng ở ván chơi vừa xong.
        /// </summary>
        /// <returns>Người chơi chiến thắng ở ván chơi vừa xong.</returns>
        public abstract Player GetVictoryPlayer();
        #endregion
    }
}
